﻿namespace NServiceBus.AcceptanceTests.Serialization;

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using AcceptanceTesting;
using AcceptanceTesting.Customization;
using Configuration.AdvancedExtensibility;
using EndpointTemplates;
using MessageInterfaces;
using NServiceBus.Serialization;
using NUnit.Framework;
using Settings;

public class When_registering_additional_deserializers : NServiceBusAcceptanceTest
{
    [Test]
    public async Task Two_endpoints_with_different_serializers_should_deserialize_the_message()
    {
        var context = await Scenario.Define<Context>()
            .WithEndpoint<CustomSerializationSender>(b => b.When(
                (session, c) =>
                {
                    var sendOptions = new SendOptions();
                    sendOptions.SetHeader("ContentType", "MyCustomSerializer");
                    return session.Send(new MyRequest());
                }))
            .WithEndpoint<XmlCustomSerializationReceiver>()
            .Done(c => c.HandlerGotTheRequest)
            .Run();

        using (Assert.EnterMultipleScope())
        {
            Assert.That(context.HandlerGotTheRequest, Is.True);
            Assert.That(context.SerializeCalled, Is.True);
            Assert.That(context.DeserializeCalled, Is.True);
            Assert.That(context.ValueFromSettings, Is.EqualTo("SomeFancySettings"));
        }
    }

    public class Context : ScenarioContext
    {
        public bool HandlerGotTheRequest { get; set; }
        public bool SerializeCalled { get; set; }
        public bool DeserializeCalled { get; set; }
        public string ValueFromSettings { get; set; }
    }

    class CustomSerializationSender : EndpointConfigurationBuilder
    {
        public CustomSerializationSender()
        {
            EndpointSetup<DefaultServer>((c, r) =>
            {
                c.UseSerialization<MyCustomSerializer>().Settings((Context)r.ScenarioContext, "");
                c.ConfigureRouting().RouteToEndpoint(typeof(MyRequest), typeof(XmlCustomSerializationReceiver));
            });
        }
    }

    class XmlCustomSerializationReceiver : EndpointConfigurationBuilder
    {
        public XmlCustomSerializationReceiver()
        {
            EndpointSetup<DefaultServer>((c, r) =>
            {
                c.UseSerialization<XmlSerializer>();
                c.AddDeserializer<MyCustomSerializer>().Settings((Context)r.ScenarioContext, "SomeFancySettings");
            });
        }

        class MyRequestHandler : IHandleMessages<MyRequest>
        {
            public MyRequestHandler(Context context)
            {
                testContext = context;
            }

            public Task Handle(MyRequest request, IMessageHandlerContext context)
            {
                testContext.HandlerGotTheRequest = true;
                return Task.CompletedTask;
            }

            Context testContext;
        }
    }

    public class MyRequest : IMessage
    {
    }

    public class MyCustomSerializer : SerializationDefinition
    {
        public override Func<IMessageMapper, IMessageSerializer> Configure(IReadOnlySettings settings)
        {
            var context = settings.Get<Context>();
            return mapper => new MyCustomMessageSerializer(context, settings.Get<string>("MyCustomSerializer.Settings.Value"));
        }
    }

    class MyCustomMessageSerializer : IMessageSerializer
    {
        public MyCustomMessageSerializer(Context context, string valueFromSettings)
        {
            this.context = context;
            this.valueFromSettings = valueFromSettings;
        }

        public void Serialize(object message, Stream stream)
        {
            var serializer = new System.Xml.Serialization.XmlSerializer(typeof(MyRequest));

            context.SerializeCalled = true;

            serializer.Serialize(stream, message);
        }

        public object[] Deserialize(ReadOnlyMemory<byte> body, IList<Type> messageTypes = null)
        {
            using (var stream = new MemoryStream(body.ToArray()))
            {
                var serializer = new System.Xml.Serialization.XmlSerializer(typeof(MyRequest));

                var msg = serializer.Deserialize(stream);
                context.DeserializeCalled = true;
                context.ValueFromSettings = valueFromSettings;

                return new[]
                {
                    msg
                };
            }
        }

        public string ContentType => "MyCustomSerializer";
        readonly Context context;
        readonly string valueFromSettings;
    }
}

static class CustomSettingsForMyCustomSerializer
{
    public static void Settings(this SerializationExtensions<When_registering_additional_deserializers.MyCustomSerializer> extensions, When_registering_additional_deserializers.Context context, string valueFromSettings)
    {
        var settings = extensions.GetSettings();
        settings.Set("MyCustomSerializer.Settings", context);
        settings.Set("MyCustomSerializer.Settings.Value", valueFromSettings);
    }
}